package cn.yx.common.core.util.image;

import cn.yx.common.core.constant.message.image.ImageUtilMessageKey;
import cn.yx.common.core.enums.MimeTypeEnum;
import cn.yx.common.core.exception.CommonException;
import cn.yx.common.core.util.io.FileUtil;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.Objects;

/**
 * <p>图片处理工具类</p>
 *
 * @author Wgssmart
 */
@Slf4j
public final class ImageUtil {

    private static final String SAVE_ERROR = "save image to path {} with type {} error: {}";

    private ImageUtil() {
    }

    /**
     * 根据指定的图片路径，获取缓冲图片
     *
     * @param imagePath 图片路径
     * @return 缓冲图对象
     */
    public static BufferedImage bufferedImage(String imagePath) {
        try (InputStream inputStream = new FileInputStream(imagePath)) {
            return ImageIO.read(inputStream);
        } catch (IOException e) {
            log.error("read image input stream error: {}, path: {}", e.getMessage(), imagePath);
            // @SimplifiedChinese=获取缓冲图像异常
            // @English=Get buffered image error
            throw new CommonException(ImageUtilMessageKey.GET_BUFFERED_IMAGE_ERROR, e);
        }
    }

    /**
     * 根据指定的图片路径，获取缓冲图片
     *
     * @param file 图片文件
     * @return 缓冲图对象
     */
    public static BufferedImage bufferedImage(File file) {
        try {
            return ImageIO.read(file);
        } catch (IOException e) {
            log.error("read image input stream error: {}, file path: {}", e.getMessage(), file.getPath());
            throw new CommonException(ImageUtilMessageKey.GET_BUFFERED_IMAGE_ERROR, e);
        }
    }

    /**
     * 根据图片输入流获取缓冲图片，不会关闭输入流参数
     *
     * @param inputStream 图片输入流
     * @return 缓冲图对象
     */
    public static BufferedImage bufferedImage(InputStream inputStream) {
        try {
            return ImageIO.read(inputStream);
        } catch (IOException e) {
            log.error("read image from input stream error: {}", e.getMessage());
            throw new CommonException(ImageUtilMessageKey.GET_BUFFERED_IMAGE_ERROR, e);
        }
    }

    /**
     * 根据图片的字节数组数据生成缓冲图
     *
     * @param imageData 图片的字节数组数据
     * @return 缓冲图对象
     */
    public static BufferedImage bufferedImage(byte[] imageData) {
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(imageData)) {
            return ImageIO.read(inputStream);
        } catch (IOException e) {
            log.error("read image from byte array input stream error: {}", e.getMessage());
            throw new CommonException(ImageUtilMessageKey.GET_BUFFERED_IMAGE_ERROR, e);
        }
    }

    /**
     * 从指定的图片url获取BufferedImage
     *
     * @param urlString 图片URL地址
     * @return
     */
    public static BufferedImage bufferedImageFromUrl(String urlString) {
        try {
            return ImageIO.read(new URL(urlString));
        } catch (IOException e) {
            log.error("get buffered image from url error: {}", e.getMessage());
            throw new CommonException(ImageUtilMessageKey.GET_BUFFERED_IMAGE_ERROR, e);
        }
    }

    /**
     * 把指定路径的图片填充到指定的输出流，不会关闭输入输出流参数
     *
     * @param imagePath 图片路径
     * @param out       输出流
     */
    public static void toOutputStream(String imagePath, OutputStream out) {
        try {
            ImageIO.write(bufferedImage(imagePath), FileUtil.getExt(imagePath), out);
        } catch (IOException e) {
            log.error("write image with file path {} to output stream error: {}", imagePath, e.getMessage());
            // @SimplifiedChinese=图像输出到指定输出流异常
            // @English=Image to output stream error
            throw new CommonException(ImageUtilMessageKey.IMAGE_TO_OUTPUT_STREAM_ERROR, e);
        }
    }

    /**
     * 根据输入流把图片内容读填充到指定的输出流，不会关闭输入输出流参数
     *
     * @param inputStream 图片输入流
     * @param out         输出流
     * @param imageType   图片类型枚举
     */
    public static void toOutputStream(InputStream inputStream, OutputStream out, MimeTypeEnum imageType) {
        try {
            ImageIO.write(bufferedImage(inputStream), imageType.getValue(), out);
        } catch (IOException e) {
            log.error("write image with type {} to output stream error: {}", imageType.getExt(), e.getMessage());
            throw new CommonException(ImageUtilMessageKey.IMAGE_TO_OUTPUT_STREAM_ERROR, e);
        }
    }

    /**
     * 根据指定的图片路径，获取图片对应的字节数组数据
     *
     * @param imagePath 图片路径
     * @return 图片对应的字节数组数据
     */
    public static byte[] imageData(String imagePath) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            toOutputStream(imagePath, outputStream);
            return outputStream.toByteArray();
        } catch (IOException e) {
            log.error("byte array output stream error: {}", e.getMessage());
            // @SimplifiedChinese=获取图像数据异常
            // @English=Get image data error
            throw new CommonException(ImageUtilMessageKey.GET_IMAGE_DATA_ERROR, e);
        }
    }

    /**
     * 根据图片输入流获取图片的字节数组数据，不会关闭输入流参数
     *
     * @param inputStream 图片输入流
     * @param imageType   图片类型枚举
     * @return 图片对应的字节数组数据
     */
    public static byte[] imageData(InputStream inputStream, MimeTypeEnum imageType) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            toOutputStream(inputStream, outputStream, imageType);
            return outputStream.toByteArray();
        } catch (IOException e) {
            log.error("byte array output stream error: {}", e.getMessage());
            throw new CommonException(ImageUtilMessageKey.GET_IMAGE_DATA_ERROR, e);
        }
    }

    /**
     * 根据图片输入流获取图片格式，如jpg, png，gif等
     *
     * @param inputStream
     * @return
     */
    public static String imageFormat(InputStream inputStream) {
        try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(inputStream)) {
            Iterator<ImageReader> iterator = ImageIO.getImageReaders(imageInputStream);
            if (iterator.hasNext()) {
                ImageReader reader = iterator.next();
                return reader.getFormatName();
            }
            return null;
        } catch (IOException e) {
            log.error("get image format from input stream error: {}", e.getMessage());
            // @SimplifiedChinese=获取图片格式异常
            // @English=Get image format error
            throw new CommonException(ImageUtilMessageKey.GET_IMAGE_FORMAT_ERROR, e);
        }
    }

    /**
     * 根据图片路径获取图片的类型枚举
     *
     * @param imagePath 图片路径
     * @return 图片类型枚举
     */
    public static MimeTypeEnum imageType(String imagePath) {
        if (imagePath.endsWith(MimeTypeEnum.PNG.getExt())) {
            return MimeTypeEnum.PNG;
        } else if (imagePath.endsWith(MimeTypeEnum.JPG.getExt())) {
            return MimeTypeEnum.JPG;
        } else if (imagePath.endsWith(MimeTypeEnum.JPEG.getExt())) {
            return MimeTypeEnum.JPEG;
        } else if (imagePath.endsWith(MimeTypeEnum.GIF.getExt())) {
            return MimeTypeEnum.GIF;
        } else if (imagePath.endsWith(MimeTypeEnum.BMP.getExt())) {
            return MimeTypeEnum.BMP;
        }
        return null;
    }

    /**
     * 根据图片输入流获取图片类型枚举
     *
     * @param inputStream
     * @return
     */
    public static MimeTypeEnum imageType(InputStream inputStream) {
        String type = imageFormat(inputStream);
        if (Objects.nonNull(type)) {
            if (MimeTypeEnum.JPEG.getValue().equalsIgnoreCase(type)) {
                return MimeTypeEnum.JPEG;
            } else if (MimeTypeEnum.PNG.getValue().equalsIgnoreCase(type)) {
                return MimeTypeEnum.PNG;
            } else if (MimeTypeEnum.GIF.getValue().equalsIgnoreCase(type)) {
                return MimeTypeEnum.GIF;
            } else if (MimeTypeEnum.BMP.getValue().equalsIgnoreCase(type)) {
                return MimeTypeEnum.BMP;
            }
        }
        return null;
    }

    /**
     * 从指定的图片url获取图片InputStream
     *
     * @param urlString
     * @return
     */
    public static InputStream inputStreamFromUrl(String urlString) {
        try {
            URLConnection urlConnection = new URL(urlString).openConnection();
            return urlConnection.getInputStream();
        } catch (IOException e) {
            log.error("get image input stream from url error: {}", e.getMessage());
            // @SimplifiedChinese=获取图像输入流异常
            // @English=Get image input stream error
            throw new CommonException(ImageUtilMessageKey.GET_IMAGE_INPUT_STREAM_ERROR, e);
        }
    }

    /**
     * 获取BufferedImage对应的InputStream
     *
     * @param bufferedImage
     * @param imageType
     * @return
     */
    public static ByteArrayInputStream inputStream(BufferedImage bufferedImage, String imageType) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            ImageIO.write(bufferedImage, imageType, byteArrayOutputStream);
            return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        } catch (IOException e) {
            log.error("write BufferedImage to ByteArrayOutputStream error: {}", e.getMessage());
            throw new CommonException(ImageUtilMessageKey.GET_IMAGE_INPUT_STREAM_ERROR, e);
        }
    }

    /**
     * 把字节数组的图片数据保存到指定路径的图片
     *
     * @param imageData 图片数据
     * @param imagePath 图片路径
     * @param imageType 图片类型枚举
     */
    public static void saveImage(byte[] imageData, String imagePath, MimeTypeEnum imageType) {
        saveImage(imageData, imagePath, imageType.getValue());
    }

    /**
     * 把字节数组的图片数据保存到指定路径的图片
     *
     * @param imageData 图片数据
     * @param toPath    图片保存路径
     * @param imageType 图片类型字符串，如png
     */
    public static void saveImage(byte[] imageData, String toPath, String imageType) {
        try {
            ImageIO.write(bufferedImage(imageData), imageType, new File(toPath));
        } catch (IOException e) {
            log.error(SAVE_ERROR, toPath, imageType, e.getMessage());
            // @SimplifiedChinese=保存图片异常
            // @English=Save image error
            throw new CommonException(ImageUtilMessageKey.SAVE_IMAGE_ERROR, e);
        }
    }

    /**
     * 把缓冲图片保存到指定路径的文件中
     *
     * @param bufferedImage 缓冲图片
     * @param toPath        图片保存路径
     * @param imageType     图片类型
     */
    public static void saveImage(BufferedImage bufferedImage, String toPath, MimeTypeEnum imageType) {
        try {
            ImageIO.write(bufferedImage, imageType.getValue(), new File(toPath));
        } catch (IOException e) {
            log.error(SAVE_ERROR, toPath, imageType.getValue(), e.getMessage());
            throw new CommonException(ImageUtilMessageKey.SAVE_IMAGE_ERROR, e);
        }
    }

    /**
     * 把图片输入流保存到指定路径的文件中
     *
     * @param inputStream 图片输入流
     * @param toPath      图片保存路径
     */
    public static void saveImage(InputStream inputStream, String toPath) {
        MimeTypeEnum mimeTypeEnum = imageType(inputStream);
        if (Objects.isNull(mimeTypeEnum)) {
            log.error("get image type from input stream error");
            // @SimplifiedChinese=获取图片类型异常
            // @English=Get image type error
            throw new CommonException(ImageUtilMessageKey.GET_IMAGE_TYPE_ERROR);
        }
        try {
            ImageIO.write(bufferedImage(inputStream), mimeTypeEnum.getValue(), new File(toPath));
        } catch (IOException e) {
            log.error(SAVE_ERROR, toPath, mimeTypeEnum.getValue(), e.getMessage());
            throw new CommonException(ImageUtilMessageKey.SAVE_IMAGE_ERROR, e);
        }
    }

    /**
     * 把指定URL地址的图片保存到指定路径的文件中
     *
     * @param urlString 图片URL地址
     * @param toPath    图片保存路径
     */
    public static void saveImage(String urlString, String toPath) {
        InputStream inputStream = inputStreamFromUrl(urlString);
        MimeTypeEnum mimeTypeEnum = imageType(inputStream);
        if (Objects.isNull(mimeTypeEnum)) {
            log.error("get image type from input stream error");
            throw new CommonException(ImageUtilMessageKey.GET_IMAGE_TYPE_ERROR);
        }
        try {
            ImageIO.write(bufferedImage(inputStream), mimeTypeEnum.getValue(), new File(toPath));
            inputStream.close();
        } catch (IOException e) {
            log.error(SAVE_ERROR, toPath, mimeTypeEnum.getValue(), e.getMessage());
            throw new CommonException(ImageUtilMessageKey.SAVE_IMAGE_ERROR, e);
        }
    }

}
